// Version 15.1
public without sharing class SilCustomerHelper 
{
    private static Boolean isFirstBefore = true;
    
    public static Boolean getIsFirstBefore(){
        return isFirstBefore;
    }
        
    public static void setIsFirstBeforeToFalse(){
        isFirstBefore = false;
    }
        
    public enum ResultCount {None, One, Multiple}
    
    public class FindResult
    {
        public ResultCount Count;
        public ID ID; 
    } 
        
    private static ID LocalAdvertiserAccountRecordType = null;
    private static ID LocalAdvertiserContactRecordType = null;

    // Returns a hash of a phone number which can be stored and used for searching. The hash of 1-800-GOFEDEX for example is 18004633339
    public static string CreatePhoneNumberHash(string phoneNumber)
    {
        string c; // holds the current character
        string hash = '';
        integer digitsFound = 0;

        if(null != phoneNumber)
        {
            // We don't check for differences between upper and lower case. So lets convert to upper case
           phoneNumber = phoneNumber.toUpperCase();           
           List<string> stringList = phoneNumber.split('|', 0);
             for(integer i=0; i < stringList.size(); i++)
            {           
                c = stringList[i];
                
                // Found a number?
                if(c >= '0' && c <= '9')
                {           
                    if(digitsFound == 0)
                    {
                        if(c == '1') // Skip inital '1' country code
                            continue;   
                    }
                    
                    hash += c;
                    digitsFound += 1;

                    continue;
                }
                
                // Convert vanity text to a number
                // Assume the vanity part does not include the area code bits or extension, and is not used in foreign numbers (apart from wonderful Canada)  
                if(digitsFound> 1 && digitsFound < 10)
                {
                    // Found a letter
                    if(c >= 'A' && c <= 'Z')
                    {
                        if(c <= 'C')
                            c = '2';
                        else if(c <= 'F')    
                            c = '3';
                        else if(c <= 'I')    
                            c = '4';
                        else if(c <= 'L')    
                            c = '5';
                        else if(c <= 'O')    
                            c = '6';
                        else if(c <= 'S')    
                            c = '7';
                        else if(c <= 'V')    
                            c = '8';
                        else     
                            c = '9';
    
                        hash += c;
                        digitsFound += 1;                                           
                        continue;
                    }               
                }
            }           
        }
        
        if(digitsFound < 10)
            hash = null;
        
        return hash;
    }
    
    
    // Returns a map that has the account associated with each phone number, or multiple/none if there is more/less than one.   
    public static Map<string,FindResult>  FindAccountByPhoneNumber(Set<string> phoneNumberHashes)
    {
        Map<string,FindResult> result = new Map<string,FindResult>();
        
        FindResult notFoundResult = new FindResult();
        notFoundResult.Count = ResultCount.None;
                
        for(string phoneNumber : phoneNumberHashes)
        {
            if(phoneNumber != null && phoneNumber.length() > 0)
                result.put(phoneNumber.ToUpperCase(), notFoundResult);
        }       
            
        ID recordTypeId = GetLocalAdvertiserAccountRecordType();
        List<Account> accounts = [Select ID, CleanPhoneText__c From Account where CleanPhoneText__c IN  :result.keySet()  and RecordTypeId = :recordTypeId];

        FindResult r ;
        
        for(Account a  : accounts)
        {
                 r = result.Get(a.CleanPhoneText__c);
                if(r.Count == ResultCount.None)
                {
                    r = new FindResult();
                    r.Count = ResultCount.One;
                    r.ID = a.ID;
                    result.Put(a.CleanPhoneText__c, r);
                }
                else if (r.Count == ResultCount.One)
                {
                    r.Count = ResultCount.Multiple;
                }
        }

        return result;
    }

    // Returns a map that has the account associated with each phone number, or multiple/none if there is more/less than one.   
    public static Map<string,FindResult>  FindAccountByContactPhoneNumber(Set<string> phoneNumberHashes)
    {
        Map<string,FindResult> result = new Map<string,FindResult>();
        
        FindResult notFoundResult = new FindResult();
        notFoundResult.Count = ResultCount.None;
                
        for(string phoneNumber : phoneNumberHashes)
        {

            if(phoneNumber != null && phoneNumber.length() > 0)
                result.put(phoneNumber.ToUpperCase(), notFoundResult);
        }       
        
        ID recordTypeId = GetLocalAdvertiserContactRecordType();
        List<Contact> contacts = [Select Account.ID, CleanPhoneText__c From Contact where CleanPhoneText__c IN  :result.keySet()  and RecordTypeId = :recordTypeId and Account.ID != null];

        FindResult r ;
        
        for(Contact c  : contacts)
        {
                 r = result.Get(c.CleanPhoneText__c);
                if(r.Count == ResultCount.None)
                {
                    r = new FindResult();
                    r.Count = ResultCount.One;
                    r.ID = c.Account.ID;
                    result.Put(c.CleanPhoneText__c, r);
                }
                else if (r.Count == ResultCount.One && r.ID != c.Account.ID)
                {
                    r.Count = ResultCount.Multiple;
                }
        }
        
        return result;
    }

    // Returns a map that has the account associated with each phone number and name, or multiple/none if there is more/less than one.  
    public static Map<string,Map<string,FindResult>>  FindAccountByPhoneNumberAndName(Set<string> phoneNumberHashes, Set<string> names)
    {       
        Map<string,Map<string,FindResult>> result = new Map<string,Map<string,FindResult>>();

        FindResult notFoundResult = new FindResult();
        notFoundResult.Count = ResultCount.None;
                
        List<string> phonesToCheck = new List<string>();
        List<string> namesToCheck = new List<string>();
        
        for(string phoneNumber : phoneNumberHashes)
        {   
            if(phoneNumber != null && phoneNumber.length() > 0)
                phonesToCheck.add(phoneNumber);         
        }       

        for(string name : names)
        {
            if(name != null && name.length() > 0)
                namesToCheck.add(name);
        }       
        
        ID recordTypeId = GetLocalAdvertiserAccountRecordType();
        List<Account> accounts = [Select ID, CleanPhoneText__c, Name From Account where CleanPhoneText__c IN  :phonesToCheck and name in :namesToCheck and CleanPhoneText__c != null  and RecordTypeId = :recordTypeId];

        Map<string,FindResult> nameMap;
        FindResult r;
            
        for(Account a  : accounts)
        {
                 nameMap = result.Get(a.CleanPhoneText__c);
                                                    
                if(nameMap == null)
                {
                    nameMap = new Map<string,FindResult>();
                    result.Put(a.CleanPhoneText__c, nameMap);
                }
                
                r = nameMap.Get(a.Name.ToUpperCase());
                
                if(r == null)
                {
                    r = new FindResult();
                    r.Count = ResultCount.One;
                    r.ID = a.ID;                    
                    nameMap.put(a.Name.ToUpperCase(), r);
                }
                else if (r.Count == ResultCount.One)
                {
                    r.Count = ResultCount.Multiple;
                }
        }

        return result;
    }
    
    // Returns a map that has the account associated with each phone number and name, or multiple/none if there is more/less than one.  
    public static Map<string,Map<string,FindResult>>  FindAccountByContactPhoneNumberAndAccountName(Set<string> phoneNumberHashes, Set<string> names)
    {       
        Map<string,Map<string,FindResult>> result = new Map<string,Map<string,FindResult>>();

        FindResult notFoundResult = new FindResult();
        notFoundResult.Count = ResultCount.None;
                
        List<string> phonesToCheck = new List<string>();
        List<string> namesToCheck = new List<string>();
        
        for(string phoneNumber : phoneNumberHashes)
        {
            if(phoneNumber != null && phoneNumber.length() > 0)
                phonesToCheck.add(phoneNumber);         
        }       

        for(string name : names)
        {
            if(name != null && name.length() > 0)
                namesToCheck.add(name);
        }       

        ID accountRecordTypeId = GetLocalAdvertiserAccountRecordType();
        List<Account> accounts = [Select ID  From Account where name in :namesToCheck  and RecordTypeId = :accountRecordTypeId];
        
        ID contactRecordTypeId = GetLocalAdvertiserContactRecordType();
        List<Contact> contact = [Select Account.ID, CleanPhoneText__c, Account.Name From Contact where CleanPhoneText__c IN  :phonesToCheck and Account.ID in :accounts  and CleanPhoneText__c != null  and Account.Name != null and RecordTypeId = :contactRecordTypeId];

        Map<string,FindResult> nameMap;
        FindResult r;
            
        for(Contact c  : contact)
        {
                 nameMap = result.Get(c.CleanPhoneText__c);
                                                    
                if(nameMap == null)
                {
                    nameMap = new Map<string,FindResult>();
                    result.Put(c.CleanPhoneText__c, nameMap);
                }
                
                r = nameMap.Get(c.Account.Name.ToUpperCase());
                
                if(r == null)
                {
                    r = new FindResult();
                    r.Count = ResultCount.One;
                    r.ID = c.Account.ID;                    
                    nameMap.put(c.Account.Name.ToUpperCase(), r);
                }
                else if (r.Count == ResultCount.One && r.ID != c.Account.ID)
                {
                    r.Count = ResultCount.Multiple;
                }
        }

        return result;
    }

    // Returns a map that has the advertiser account associated with each company name, or multiple/none if there is more/less than one.    
    public static  Map<string,FindResult> FindAdvertiserAccountByName(Set<string> names)
    {
        Map<string,FindResult> result = new Map<string,FindResult>();
        
        FindResult notFoundResult = new FindResult();
        notFoundResult.Count = ResultCount.None;
                
        for(string name : names)
        {
            if(name != null && name.length() > 0)
            {
                result.put(name.ToUpperCase(), notFoundResult);
            }
        }       
            
        ID recordTypeId = GetLocalAdvertiserAccountRecordType();
        List<Account> accounts = [Select ID, Name  From Account where Name  IN  :result.keySet()  and RecordTypeId = :recordTypeId];

        FindResult r ;      
        for(Account a  : accounts)
        {
                 r = result.Get(a.name.ToUpperCase());
                 
                if(r.Count == ResultCount.None)
                {
                    r = new FindResult();
                    r.Count = ResultCount.One;
                    r.ID = a.ID;
                    result.Put(a.name.ToUpperCase(), r);
                }
                else if (r.Count == ResultCount.One)
                {
                    r.Count = ResultCount.Multiple;
                }
        }

        return result;
    }

    // Returns a map that has the  account associated with and AFrontier Customer ID, or multiple/none if there is more/less than one.  
    public static  Map<string,FindResult> FindAccountByAFrontierCustomerId(Set<string> afrontierCustomerIds)
    {
        Map<string,FindResult> result = new Map<string,FindResult>();
        
        FindResult notFoundResult = new FindResult();
        notFoundResult.Count = ResultCount.None;
                
        for(string afrontierCustomerId : afrontierCustomerIds)
        {
            if(afrontierCustomerId != null && afrontierCustomerId.length() > 0)
                result.put(afrontierCustomerId.ToUpperCase(), notFoundResult);
        }       
        
        ID recordTypeId = GetLocalAdvertiserAccountRecordType();
        List<Account> accounts = [Select ID, AFrontier_CustomerID__c  From Account where AFrontier_CustomerID__c  IN  :result.keySet()   and RecordTypeId = :recordTypeId];

        FindResult r ;      
        for(Account a  : accounts)
        {
                 r = result.Get(a.AFrontier_CustomerID__c.ToUpperCase());
                if(r.Count == ResultCount.None)
                {
                    r = new FindResult();
                    r.Count = ResultCount.One;
                    r.ID = a.ID;
                    result.Put(a.AFrontier_CustomerID__c.ToUpperCase(), r);
                }
                else if (r.Count == ResultCount.One)
                {
                    r.Count = ResultCount.Multiple;
                }
        }

        return result;
    }
    
    public  static ID GetLocalAdvertiserAccountRecordType()
    {
        if(localAdvertiserAccountRecordType == null)
            localAdvertiserAccountRecordType = RecordTypeHelper.GetRecordTypeID('Account', 'Local OOR Customer');
        
        return localAdvertiserAccountRecordType;
    }

    public  static ID GetLocalAdvertiserContactRecordType()
    {
        if(localAdvertiserContactRecordType == null)
            localAdvertiserContactRecordType = RecordTypeHelper.GetRecordTypeID('Contact', 'Local Advertiser');
        
        return localAdvertiserContactRecordType;
    }
}